package org.hekang;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.StringUtils;
import org.hekang.entity.Cat;
import org.hekang.entity.PrivateCat;
import org.hekang.entity.QueryDB;
import org.hekang.result.CatQueryPageResult;
import org.hekang.result.CatQueryResult;
import org.hekang.result.CatQueryResultList;
import org.hyperledger.fabric.contract.Context;
import org.hyperledger.fabric.contract.ContractInterface;
import org.hyperledger.fabric.contract.annotation.*;
import org.hyperledger.fabric.shim.ChaincodeException;
import org.hyperledger.fabric.shim.ChaincodeStub;
import org.hyperledger.fabric.shim.ledger.KeyValue;
import org.hyperledger.fabric.shim.ledger.QueryResultsIterator;
import org.hyperledger.fabric.shim.ledger.QueryResultsIteratorWithMetadata;

import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * 描述: 猫的合约
 *
 * @author hekang
 * @date 2024-04-18 09:56
 */
@Contract(name = "CatContract",
        info = @Info(
                title = "Cat contract",
                description = "The hyperlegendary car contract",
                version = "0.0.1-SNAPSHOT",
                license = @License(
                        name = "Apache 2.0 License",
                        url = "http://www.apache.org/licenses/LICENSE-2.0.html"),
                contact = @Contact(
                        email = "f.carr@example.com",
                        name = "F Carr",
                        url = "https://hyperledger.example.com")))
@Default
public class CatContract implements ContractInterface {
    /**
     * 描述: 初始化合约
     *
     * @param ctx 合约上下文
     * @returns void
     * @author hekang
     * @date 2024/4/18 10:27
     */
    @Transaction
    public void initLedger(final Context ctx) {
        ChaincodeStub ctxStub = ctx.getStub();
        // 设置10只猫
        for (int i = 0; i < 10; i++) {
            Cat cat = new Cat().setName("cat-" + i)
                    .setAge(new Random().nextInt(20) + 1)
                    .setBreed("橘猫")
                    .setColor("橘黄色");
            // 设置数据  实体转字符串
            ctxStub.putStringState(cat.getName(), JSON.toJSONString(cat));
        }
    }

    /**
     * 描述: 根据key查询猫
     *
     * @param ctx 合约上下文
     * @param key key
     * @returns org.hkfabric.entity.Cat
     * @author hekang
     * @date 2024/4/18 10:32
     */
    @Transaction
    public Cat queryCatByKey(final Context ctx, final String key) {
        ChaincodeStub ctxStub = ctx.getStub();
        String catStringState = ctxStub.getStringState(key);
        // 数据不存在
        if (StringUtils.isBlank(catStringState)) {
            String errorMessage = String.format("Cat %s does not exist", key);
            System.out.println(errorMessage);
            throw new ChaincodeException(errorMessage);
        }
        return JSON.parseObject(catStringState, Cat.class);
    }

    /**
     * 描述: 新增一只猫
     *
     * @param ctx
     * @param key
     * @param name
     * @param age
     * @param color
     * @param breed
     * @returns org.hkfabric.entity.Cat
     * @author hekang
     * @date 2024/4/18 10:40
     */
    @Transaction
    public Cat createCat(final Context ctx, final String key, String name, Integer age, String color, String breed) {

        ChaincodeStub stub = ctx.getStub();
        String catState = stub.getStringState(key);

        if (StringUtils.isNotBlank(catState)) {
            String errorMessage = String.format("Cat %s already exists", key);
            System.out.println(errorMessage);
            throw new ChaincodeException(errorMessage);
        }

        Cat cat = new Cat().setName(name)
                .setAge(age)
                .setBreed(breed)
                .setColor(color);

        String json = JSON.toJSONString(cat);
        stub.putStringState(key, json);

        stub.setEvent("createCatEvent", org.apache.commons.codec.binary.StringUtils.getBytesUtf8(json));
        return cat;
    }

    /**
     * 描述: 根据key更新一只猫
     *
     * @param ctx
     * @param key
     * @param name
     * @param age
     * @param color
     * @param breed
     * @returns org.hkfabric.entity.Cat
     * @author hekang
     * @date 2024/4/18 10:40
     */
    @Transaction
    public Cat updateCat(final Context ctx, final String key, String name, Integer age, String color, String breed) {

        ChaincodeStub stub = ctx.getStub();
        String catState = stub.getStringState(key);

        if (StringUtils.isBlank(catState)) {
            String errorMessage = String.format("Cat %s does not exist", key);
            System.out.println(errorMessage);
            throw new ChaincodeException(errorMessage);
        }

        Cat cat = new Cat().setName(name)
                .setAge(age)
                .setBreed(breed)
                .setColor(color);

        stub.putStringState(key, JSON.toJSONString(cat));

        return cat;
    }

    /**
     * 描述: 删除一只猫
     *
     * @param ctx
     * @param key
     * @returns org.hkfabric.entity.Cat
     * @author hekang
     * @date 2024/4/18 10:40
     */
    @Transaction
    public Cat deleteCat(final Context ctx, final String key) {

        ChaincodeStub stub = ctx.getStub();
        String catState = stub.getStringState(key);

        if (StringUtils.isBlank(catState)) {
            String errorMessage = String.format("Cat %s does not exist", key);
            System.out.println(errorMessage);
            throw new ChaincodeException(errorMessage);
        }

        stub.delState(key);

        return JSON.parseObject(catState, Cat.class);
    }


    //------------------------私有数据函数--------------------------

    /**
     * 描述: 根据key查询私有数据
     *
     * @param ctx
     * @param collection
     * @param key
     * @returns byte[]
     * @author hekang
     * @date 2024/4/25 16:28
     */
    @Transaction
    public PrivateCat queryPrivateCatByKey(final Context ctx, final String collection, final String key) {
        ChaincodeStub stub = ctx.getStub();
        String catState = stub.getPrivateDataUTF8(collection, key);
        if (StringUtils.isBlank(catState)) {
            throw new ChaincodeException(String.format("Private Cat %s does not exist", key));
        }
        return JSON.parseObject(catState, PrivateCat.class);
    }

    /**
     * 描述: 新增一只私有数据
     *
     * @param ctx
     * @param collection
     * @param key
     * @param name
     * @param age
     * @param color
     * @param breed
     * @returns org.hekang.entity.Cat
     * @author hekang
     * @date 2024/4/25 16:30
     */
    @Transaction
    public PrivateCat createPrivateCat(final Context ctx, final String collection, final String key, String name, Integer age, String color, String breed) {

        ChaincodeStub stub = ctx.getStub();
        String catState = stub.getPrivateDataUTF8(collection, key);

        if (StringUtils.isNotBlank(catState)) {
            throw new ChaincodeException(String.format("Cat %s already exists", key));
        }

        PrivateCat privateCat = new PrivateCat().setCat(new Cat().setName(name)
                .setAge(age)
                .setBreed(breed)
                .setColor(color)).setCollection(collection);

        String json = JSON.toJSONString(privateCat);
        stub.putPrivateData(collection, key, json);
        return privateCat;
    }

    /**
     * 描述: 删除一只私有数据
     *
     * @param ctx
     * @param collection
     * @param key
     * @param name
     * @param age
     * @param color
     * @param breed
     * @returns org.hekang.entity.Cat
     * @author hekang
     * @date 2024/4/25 16:34
     */
    @Transaction
    public PrivateCat updatePrivateCat(final Context ctx, final String collection, final String key, String name, Integer age, String color, String breed) {

        ChaincodeStub stub = ctx.getStub();
        String catState = stub.getPrivateDataUTF8(collection, key);

        if (StringUtils.isNotBlank(catState)) {
            throw new ChaincodeException(String.format("Cat %s already exists", key));
        }

        PrivateCat privateCat = new PrivateCat().setCat(new Cat().setName(name)
                .setAge(age)
                .setBreed(breed)
                .setColor(color)).setCollection(collection);

        stub.putPrivateData(collection, key, JSON.toJSONString(privateCat));
        return privateCat;
    }

    /**
     * 描述: 删除一只私有数据
     *
     * @param ctx
     * @param collection
     * @param key
     * @returns org.hekang.entity.Cat
     * @author hekang
     * @date 2024/4/25 16:35
     */
    @Transaction
    public PrivateCat deletePrivateCat(final Context ctx, final String collection, final String key) {

        ChaincodeStub stub = ctx.getStub();
        String catState = stub.getPrivateDataUTF8(collection, key);

        if (StringUtils.isBlank(catState)) {
            throw new ChaincodeException(String.format("Cat %s already exists", key));
        }

        stub.delPrivateData(catState, key);

        return JSON.parseObject(catState, PrivateCat.class);
    }

    //----------------------------couchdb查询-------------------------------

    /**
     * 描述: 根据名称查询猫
     *
     * @param ctx
     * @param name
     * @returns org.hkfabric.result.CatQueryResultList
     * @author hekang
     * @date 2024/4/24 10:47
     */
    @Transaction
    public CatQueryResultList queryCatByName(final Context ctx, String name) {
        // 查询json   参考：https://hyperledger-fabric.readthedocs.io/zh-cn/latest/couchdb_tutorial.html?highlight=use_index
        QueryDB queryDB = new QueryDB();
        queryDB.setSelector(new Cat().setName(name));
        return queryCat(ctx.getStub(), JSON.toJSONString(queryDB));
    }
    
    /**
     * 描述: 根据名称和颜色查询
     *
     * @param ctx
     * @param name
     * @param color
     * @returns org.hkfabric.result.CatQueryResultList
     * @author hekang
     * @date 2024/4/24 11:19
     */
    @Transaction
    public CatQueryResultList queryCatByNameAndColor(final Context ctx, String name, String color) {
        // 查询json   参考：https://hyperledger-fabric.readthedocs.io/zh-cn/latest/couchdb_tutorial.html?highlight=use_index
        QueryDB queryDB = new QueryDB();
        queryDB.setSelector(new Cat().setName(name).setColor(color));
        queryDB.setUse_index(Arrays.asList("_design/indexNameColorDoc", "indexNameColor"));
        return queryCat(ctx.getStub(), JSON.toJSONString(queryDB));
    }

    /**
     * 描述: 带分页查询
     *
     * @param ctx
     * @param name
     * @param pageSize
     * @param bookmark
     * @returns org.hkfabric.result.CatQueryPageResult
     * @author hekang
     * @date 2024/4/24 11:23
     */
    @Transaction
    public CatQueryPageResult queryCatPageByName(final Context ctx, String name, Integer pageSize, String bookmark) {

        QueryDB queryDB = new QueryDB();
        queryDB.setSelector(new Cat().setName(name));
        //  索引
        queryDB.setUse_index(Arrays.asList("_design/indexNameColorDoc", "indexNameColor"));

        ChaincodeStub stub = ctx.getStub();
        //  分页查询
        QueryResultsIteratorWithMetadata<KeyValue> queryResult = stub.getQueryResultWithPagination(JSON.toJSONString(queryDB), pageSize, bookmark);

        List<CatQueryResult> cats = Lists.newArrayList();

        if (!IterableUtils.isEmpty(queryResult)) {
            for (KeyValue kv : queryResult) {
                cats.add(new CatQueryResult().setKey(kv.getKey()).setCat(JSON.parseObject(kv.getStringValue(), Cat.class)));
            }
        }

        return new CatQueryPageResult()
                .setCats(cats)
                .setBookmark(queryResult.getMetadata().getBookmark());
    }

    /**
     * 描述: 通用查询
     *
     * @param stub
     * @param query
     * @returns org.hkfabric.result.CatQueryResultList
     * @author hekang
     * @date 2024/4/24 10:39
     */
    private CatQueryResultList queryCat(ChaincodeStub stub, String query) {

        CatQueryResultList resultList = new CatQueryResultList();
        QueryResultsIterator<KeyValue> queryResult = stub.getQueryResult(query);
        List<CatQueryResult> results = Lists.newArrayList();

        if (!IterableUtils.isEmpty(queryResult)) {
            for (KeyValue kv : queryResult) {
                results.add(new CatQueryResult().setKey(kv.getKey()).setCat(JSON.parseObject(kv.getStringValue(), Cat.class)));
            }
            resultList.setCats(results);
        }

        return resultList;
    }

}